/*
 *  Copyright 2012 Anton Van Zyl. http://code.google.com/p/java-swiss-knife/
 * 
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 * 
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *  under the License.
 */
package com.knife.web.threading;

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;

import sun.misc.BASE64Encoder;

import com.knife.web.threading.exception.InvalidRequestTokenException;

/**
 * This is prevention class to enable us to prevent a double submit in critical
 * section, for instance; a payment in a banking web application.<br/>
 * The idea around this is using a unique key in a flow and only allowing a flow
 * to continue with the unique key, if the flow does not contain the unique key,
 * the user is seen as a double submit, and the input is not accepted and the
 * output is handles, be it a error screen or going to a result page(which would
 * bring up other problems with data, overcome this by rather using
 * DoubleSubmitLock).
 * 
 * <br/>
 * <br/>
 * Please visit <a
 * href="http://code.google.com/p/java-swiss-knife/">Java-Swiss-Knife</a> and
 * comment, rate, contribute or raise a issue/enhancement for my library. <br/>
 * 
 * @author Anton Van Zyl
 * 
 */
public class DoubleSubmitBlock {

	/**
	 * Generates a random Token and adds it to the session to be used in
	 * validating incoming requests.<br/>
	 * Use the validateUniqueRequestIdentifier method to validate the key value
	 * against the session.<br/>This is a thread safe method.
	 * <b>SessionKey = requestIdentifierSessionKey</b>
	 * 
	 * @param request - The <code>HttpServletRequest</code> as input
	 * @param requestIdentifierSessionKey - Sets the Unique key in the session with <code>UniqueRequestIdentifier</code> as the key
	 */
	public static void setUniqueRequestIdenitfier(HttpServletRequest request, String requestIdentifierSessionKey) throws NoSuchAlgorithmException {
		synchronized (request.getSession()) {

			// Generate the SHA1 encoded String
			SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
			byte[] bytes = new byte[30];
			sr.nextBytes(bytes);

			request.getSession().setAttribute(requestIdentifierSessionKey, String.valueOf(new BASE64Encoder().encode(bytes)));

		}
	}

	/**
	 * This will validate the key in the session and if invalid throw an
	 * exception to be handled.<br/> This is a thread safe method.
	 * 
	 * @param key - the input that came from the page for the session key
	 * @param request - The <code>HttpServletRequest</code> as input
	 * @param requestIdentifierSessionKey - Sets the Unique key in the session with <code>UniqueRequestIdentifier</code> as the key
	 * @throws InvalidRequestTokenException
	 * @throws InterruptedException
	 */
	public static void validateUniqueRequestIdentifier(String key, HttpServletRequest request, String requestIdentifierSessionKey) throws InvalidRequestTokenException,
			InterruptedException {
		synchronized (request.getSession()) {
			if (!StringUtils.equals(key, String.valueOf(request.getSession().getAttribute(requestIdentifierSessionKey)))) {
				throw new InvalidRequestTokenException("User does not have token in session");
			}
		}
	}

	/**
	 * This will validate the key in the session and if invalid throw an
	 * exception to be handled.<br/>
	 * requestIdentifierSessionKey is removed after a valid request key is
	 * found. This is a thread safe method.
	 * 
	 * @param key - the input that came from the page for the session key
	 * @param request - The <code>HttpServletRequest</code> as input
	 * @param requestIdentifierSessionKey - Sets the Unique key in the session with <code>UniqueRequestIdentifier</code> as the key
	 * @throws InvalidRequestTokenException
	 * @throws InterruptedException
	 */
	public static void validateAndRemoveUniqueRequestIdentifier(String key, HttpServletRequest request, String requestIdentifierSessionKey) throws InvalidRequestTokenException,
			InterruptedException {
		synchronized (request.getSession()) {

			if (!StringUtils.equals(key, String.valueOf(request.getSession().getAttribute(requestIdentifierSessionKey)))) {
				throw new InvalidRequestTokenException("User does not have token in session");
			}
			request.getSession().removeAttribute(requestIdentifierSessionKey);
		}
	}

}
